package com.springboot.security.filter;

import com.springboot.security.entity.Menu;
import com.springboot.security.entity.Role;
import com.springboot.security.mapper.MenuMapper;
import com.sun.org.apache.bcel.internal.generic.NEW;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.Collection;
import java.util.List;

/**
 * 要实现动态配置权限，首先要自定义 FilterInvocationSecurityMetadataSource，Spring Security 中通过
 * FilterInvocationSecurityMetadataSource接口中的 getAttributes 方法来确定一个请求需要哪些角色，
 * FilterInvocationSecurityMetadataSource 接口的默认实现类是 DefaultFilterInvocationSecurityMetadataSource，
 * 参考次实现类，可以自定义自己的实现类，代码如下
 *
 * 自定义 FilterInvocationSecurityMetadataSource 主要实现该接口中的 getAttributes 方法，该方法的参数是一个 FilterInvocation，
 * 开发者可以从 FilterInvocation 中提取当前请求的 URL，返回值为 Collection<ConfigAttribute> ,表示当前请求 URL 所需的角色。
 *
 * 当一个请求走完 FilterInvocationSecurityMetadataSource 中的 getAttributes 方法后，接在来会来到 AccessDecisionManager 类中
 * 进行角色信息的比对，
 * @author wangning
 * @create 2021-05-21 11:11
 */
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
	//创建一个 AntPathMatcher，主要用来实现 ant 风格的 URL 匹配
	AntPathMatcher antPathMatcher = new AntPathMatcher();
	@Autowired
	private MenuMapper menuMapper;

	@Override
	public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
		//从参数中提取出当前请求的 URL
		String requestUrl = ((FilterInvocation) object).getRequestUrl();
		List<Menu> allMenus = menuMapper.getAllMenus();
		//遍历过程中获取当前请求的 URL 所需的角色信息并返回，如果当前请求的 URL 在资源列表中不存在响应的模式，
		//就假设该请求登录后即可访问，即直接返回 ROLE_LOGIN
		for (Menu menu : allMenus) {
			if (antPathMatcher.match(menu.getPattern(), requestUrl)) {
				List<Role> roles = menu.getRoles();
				String[] roleArr = new String[roles.size()];
				for (int i = 0; i < roleArr.length; i++) {
					roleArr[i] = roles.get(i).getName();
				}
				return SecurityConfig.createList(roleArr);
			}
		}
		return SecurityConfig.createList("ROLE_LOGIN");
	}

	/**
	 * getAllConfigAttributes 方法用来返回所有定义好的权限资源，Spring Security 在启动时会校验相关的配置是否正确，如果不需要
	 * 检验，直接返回 null 即可
	 * @return
	 */
	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return null;
	}

	/**
	 * supports 方法返回类对象是否支持校验
	 * @param clazz
	 * @return
	 */
	@Override
	public boolean supports(Class<?> clazz) {
		return FilterInvocation.class.isAssignableFrom(clazz);
	}

}
